<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1, user-scalable=no">
		<title>LRC滚动</title>
		<link rel="stylesheet" href="../css/hvrilsoft.spanning2022.all.css">

		<script src="./js/jquery.min.js"></script>
		<style>
			html,body{
				overflow: hidden;
				background-color: #000;
			}
			div{
				width:100%;
				height:90%;
				margin:0 auto;
				overflow:hidden;
			}
			ul{
				width:100%;
				transition-duration: 600ms;
				position: relative;
			}
			ul, li{
				width: 100%;
				list-style:none;
				padding: 0;
				margin: 0;
			}
			li{
				text-align: center;
				color: transparent;
			}
			li.on{
				font-family: "等线";
				display: block;
				color: #e60000;
				font-weight:bold;
				letter-spacing:2px;
			}
			span{
				font-family: "等线";
				overflow:hidden;
				font-weight: bold;
				height: 17px;
				line-height: 17px;
				-webkit-box-orient: vertical;
				display: -webkit-box;
				text-align: center;
				margin-top: 0;
				margin-top: 15px;
			}
		</style>
	</head>
	<body>

		<div class="hovertree"></div>


		<div style="float:left;position:absolute;width:100%;margin-top: -108px;">
			<ul id="lrclist" style="width:100%"><!-- 初始值 -->
				
			</ul>
		</div>
	
		



		<canvas id="canvas" style="width: 100%;height: 1000px;"></canvas><script>
			function initVars(){
			
				pi=Math.PI;
				ctx=canvas.getContext("2d");
				canvas.width=canvas.clientWidth;
				canvas.height=canvas.clientHeight;
				cx=canvas.width/2;
				cy=canvas.height/2;
				playerZ=-25;
				playerX=playerY=playerVX=playerVY=playerVZ=pitch=yaw=pitchV=yawV=0;
				scale=600;
				seedTimer=0;seedInterval=5,seedLife=100;gravity=.02;
				seeds=new Array();
				sparkPics=new Array();
				s="https://cantelope.org/NYE/";
				for(i=1;i<=10;++i){
					sparkPic=new Image();
					sparkPic.src=s+"spark"+i+".png";
					sparkPics.push(sparkPic);
				}
				sparks=new Array();
				pow1=new Audio(s+"pow1.ogg");
				pow2=new Audio(s+"pow2.ogg");
				pow3=new Audio(s+"pow3.ogg");
				pow4=new Audio(s+"pow4.ogg");
				frames = 0;
			}
			
			function rasterizePoint(x,y,z){
			
				var p,d;
				x-=playerX;
				y-=playerY;
				z-=playerZ;
				p=Math.atan2(x,z);
				d=Math.sqrt(x*x+z*z);
				x=Math.sin(p-yaw)*d;
				z=Math.cos(p-yaw)*d;
				p=Math.atan2(y,z);
				d=Math.sqrt(y*y+z*z);
				y=Math.sin(p-pitch)*d;
				z=Math.cos(p-pitch)*d;
				var rx1=-1000,ry1=1,rx2=1000,ry2=1,rx3=0,ry3=0,rx4=x,ry4=z,uc=(ry4-ry3)*(rx2-rx1)-(rx4-rx3)*(ry2-ry1);
				if(!uc) return {x:0,y:0,d:-1};
				var ua=((rx4-rx3)*(ry1-ry3)-(ry4-ry3)*(rx1-rx3))/uc;
				var ub=((rx2-rx1)*(ry1-ry3)-(ry2-ry1)*(rx1-rx3))/uc;
				if(!z)z=.000000001;
				if(ua>0&&ua<1&&ub>0&&ub<1){
					return {
						x:cx+(rx1+ua*(rx2-rx1))*scale,
						y:cy+y/z*scale,
						d:Math.sqrt(x*x+y*y+z*z)
					};
				}else{
					return {
						x:cx+(rx1+ua*(rx2-rx1))*scale,
						y:cy+y/z*scale,
						d:-1
					};
				}
			}
			
			function spawnSeed(){
				
				seed=new Object();
				seed.x=-50+Math.random()*100;
				seed.y=25;
				seed.z=-50+Math.random()*100;
				seed.vx=.1-Math.random()*.2;
				seed.vy=-1.5;//*(1+Math.random()/2);
				seed.vz=.1-Math.random()*.2;
				seed.born=frames;
				seeds.push(seed);
			}
			
			function splode(x,y,z){
				
				t=5+parseInt(Math.random()*150);
				sparkV=1+Math.random()*2.5;
				type=parseInt(Math.random()*3);
				switch(type){
					case 0:
						pic1=parseInt(Math.random()*10);
						break;
					case 1:
						pic1=parseInt(Math.random()*10);
						do{ pic2=parseInt(Math.random()*10); }while(pic2==pic1);
						break;
					case 2:
						pic1=parseInt(Math.random()*10);
						do{ pic2=parseInt(Math.random()*10); }while(pic2==pic1);
						do{ pic3=parseInt(Math.random()*10); }while(pic3==pic1 || pic3==pic2);
						break;
				}
				for(m=1;m<t;++m){
					spark=new Object();
					spark.x=x; spark.y=y; spark.z=z;
					p1=pi*2*Math.random();
					p2=pi*Math.random();
					v=sparkV*(1+Math.random()/6)
					spark.vx=Math.sin(p1)*Math.sin(p2)*v;
					spark.vz=Math.cos(p1)*Math.sin(p2)*v;
					spark.vy=Math.cos(p2)*v;
					switch(type){
						case 0: spark.img=sparkPics[pic1]; break;
						case 1:
							spark.img=sparkPics[parseInt(Math.random()*2)?pic1:pic2];
							break;
						case 2:
							switch(parseInt(Math.random()*3)){
								case 0: spark.img=sparkPics[pic1]; break;
								case 1: spark.img=sparkPics[pic2]; break;
								case 2: spark.img=sparkPics[pic3]; break;
							}
							break;
					}
					spark.radius=25+Math.random()*50;
					spark.alpha=1;
					spark.trail=new Array();
					sparks.push(spark);
				}
				switch(parseInt(Math.random()*4)){
					case 0:	pow=new Audio(s+"pow1.ogg"); break;
					case 1:	pow=new Audio(s+"pow2.ogg"); break;
					case 2:	pow=new Audio(s+"pow3.ogg"); break;
					case 3:	pow=new Audio(s+"pow4.ogg"); break;
				}
				d=Math.sqrt((x-playerX)*(x-playerX)+(y-playerY)*(y-playerY)+(z-playerZ)*(z-playerZ));
				pow.volume=1.5/(1+d/10);
				pow.play();
			}
			
			function doLogic(){
				
				if(seedTimer<frames){
					seedTimer=frames+seedInterval*Math.random()*10;
					spawnSeed();
				}
				for(i=0;i<seeds.length;++i){
					seeds[i].vy+=gravity;
					seeds[i].x+=seeds[i].vx;
					seeds[i].y+=seeds[i].vy;
					seeds[i].z+=seeds[i].vz;
					if(frames-seeds[i].born>seedLife){
						splode(seeds[i].x,seeds[i].y,seeds[i].z);
						seeds.splice(i,1);
					}
				}
				for(i=0;i<sparks.length;++i){
					if(sparks[i].alpha>0 && sparks[i].radius>5){
						sparks[i].alpha-=.01;
						sparks[i].radius/=1.02;
						sparks[i].vy+=gravity;
						point=new Object();
						point.x=sparks[i].x;
						point.y=sparks[i].y;
						point.z=sparks[i].z;
						if(sparks[i].trail.length){
							x=sparks[i].trail[sparks[i].trail.length-1].x;
							y=sparks[i].trail[sparks[i].trail.length-1].y;
							z=sparks[i].trail[sparks[i].trail.length-1].z;
							d=((point.x-x)*(point.x-x)+(point.y-y)*(point.y-y)+(point.z-z)*(point.z-z));
							if(d>9){
								sparks[i].trail.push(point);
							}
						}else{
							sparks[i].trail.push(point);
						}
						if(sparks[i].trail.length>5)sparks[i].trail.splice(0,1);				
						sparks[i].x+=sparks[i].vx;
						sparks[i].y+=sparks[i].vy;
						sparks[i].z+=sparks[i].vz;
						sparks[i].vx/=1.075;
						sparks[i].vy/=1.075;
						sparks[i].vz/=1.075;
					}else{
						sparks.splice(i,1);
					}
				}
				p=Math.atan2(playerX,playerZ);
				d=Math.sqrt(playerX*playerX+playerZ*playerZ);
				d+=Math.sin(frames/80)/1.25;
				t=Math.sin(frames/200)/40;
				playerX=Math.sin(p+t)*d;
				playerZ=Math.cos(p+t)*d;
				yaw=pi+p+t;
			}
			
			function rgb(col){
				
				var r = parseInt((.5+Math.sin(col)*.5)*16);
				var g = parseInt((.5+Math.cos(col)*.5)*16);
				var b = parseInt((.5-Math.sin(col)*.5)*16);
				return "#"+r.toString(16)+g.toString(16)+b.toString(16);
			}
			
			function draw(){
				
				ctx.clearRect(0,0,cx*2,cy*2);
				
				ctx.fillStyle="#ff8";
				for(i=-100;i<100;i+=3){
					for(j=-100;j<100;j+=4){
						x=i;z=j;y=25;
						point=rasterizePoint(x,y,z);
						if(point.d!=-1){
							size=250/(1+point.d);
							d = Math.sqrt(x * x + z * z);
							a = 0.75 - Math.pow(d / 100, 6) * 0.75;
							if(a>0){
								ctx.globalAlpha = a;
								ctx.fillRect(point.x-size/2,point.y-size/2,size,size);				
							}
						}
					}
				}
				ctx.globalAlpha=1;
				for(i=0;i<seeds.length;++i){
					point=rasterizePoint(seeds[i].x,seeds[i].y,seeds[i].z);
					if(point.d!=-1){
						size=200/(1+point.d);
						ctx.fillRect(point.x-size/2,point.y-size/2,size,size);
					}
				}
				point1=new Object();
				for(i=0;i<sparks.length;++i){
					point=rasterizePoint(sparks[i].x,sparks[i].y,sparks[i].z);
					if(point.d!=-1){
						size=sparks[i].radius*200/(1+point.d);
						if(sparks[i].alpha<0)sparks[i].alpha=0;
						if(sparks[i].trail.length){
							point1.x=point.x;
							point1.y=point.y;
							switch(sparks[i].img){
								case sparkPics[0]:ctx.strokeStyle="#f84";break;
								case sparkPics[1]:ctx.strokeStyle="#84f";break;
								case sparkPics[2]:ctx.strokeStyle="#8ff";break;
								case sparkPics[3]:ctx.strokeStyle="#fff";break;
								case sparkPics[4]:ctx.strokeStyle="#4f8";break;
								case sparkPics[5]:ctx.strokeStyle="#f44";break;
								case sparkPics[6]:ctx.strokeStyle="#f84";break;
								case sparkPics[7]:ctx.strokeStyle="#84f";break;
								case sparkPics[8]:ctx.strokeStyle="#fff";break;
								case sparkPics[9]:ctx.strokeStyle="#44f";break;
							}
							for(j=sparks[i].trail.length-1;j>=0;--j){
								point2=rasterizePoint(sparks[i].trail[j].x,sparks[i].trail[j].y,sparks[i].trail[j].z);
								if(point2.d!=-1){
									ctx.globalAlpha=j/sparks[i].trail.length*sparks[i].alpha/2;
									ctx.beginPath();
									ctx.moveTo(point1.x,point1.y);
									ctx.lineWidth=1+sparks[i].radius*10/(sparks[i].trail.length-j)/(1+point2.d);
									ctx.lineTo(point2.x,point2.y);
									ctx.stroke();
									point1.x=point2.x;
									point1.y=point2.y;
								}
							}
						}
						ctx.globalAlpha=sparks[i].alpha;
						ctx.drawImage(sparks[i].img,point.x-size/2,point.y-size/2,size,size);
					}
				}
			}
			
			function frame(){
			
				if(frames>100000){
					seedTimer=0;
					frames=0;
				}
				frames++;
				draw();
				doLogic();
				requestAnimationFrame(frame);
			}
			
			window.addEventListener("resize",()=>{
				canvas.width=canvas.clientWidth;
				canvas.height=canvas.clientHeight;
				cx=canvas.width/2;
				cy=canvas.height/2;
			});
			
			initVars();
			frame();</script>




		<script>
		
		var lrcJSON = {
			"[00:00.00]":"除夕",
"[00:00.79]":"词 Lyricist：偏生梓归",
"[00:01.18]":"曲 Composer：殇小谨",
"[00:01.75]":"演唱 Vocal：音阙诗听/A-SOUL",
"[00:02.19]":"编曲 Arrangement：于瑾",
"[00:02.75]":"制作人 Producer ：郭亚男", 
"[00:03.55]":"监唱制作人 Vocal Producer：郭亚男", 
"[00:04.04]":"音乐编辑 Audio Editing：石行@维伴音乐",
"[00:04.48]":"混音 Mixing Engineer：黄可爱@维伴音乐",
"[00:05.04]":"和声编写 Backing Vocal Arrange：郭亚男", 
"[00:05.74]":"和声 Backing Vocal Performe：A-SOUL 郭亚男",
"[00:06.21]":"统筹 A&R：梓七",
"[00:06.58]":"（版权所有，未经许可请勿使用）",
"[00:07.30]":"在不够天晴的夜晚",
"[00:10.36]":"灯笼为谁挂一盏",
"[00:14.23]":"对联绕个弯没贴完",
"[00:17.18]":"落雪飘摇到漫漫",
"[00:21.12]":"路途辗转",
"[00:22.87]":"何时到站",
"[00:24.57]":"家人殷切的问探",
"[00:28.07]":"来去一趟",
"[00:29.80]":"归心似箭",
"[00:31.50]":"心底默默盼",
"[00:35.04]":"一盼有你陪伴",
"[00:38.31]":"一盼更果敢",
"[00:41.71]":"一盼人生圆满",
"[00:45.28]":"一盼皆平安",
"[00:47.80]":"除夕当庆贺",
"[00:48.87]":"走街串巷过",
"[00:51.04]":"除夕当庆贺",
"[00:52.21]":"知足更常乐",
"[00:54.65]":"除夕当庆贺",
"[00:55.77]":"能与你度过",
"[00:58.56]":"这团圆时刻",
"[00:59.71]":"年夜饭摆一桌",
"[01:01.52]":"除夕当庆贺",
"[01:02.62]":"腊梅照春色",
"[01:05.09]":"除夕当庆贺",
"[01:06.14]":"守岁望烟火",
"[01:08.49]":"除夕当庆贺",
"[01:09.50]":"与你游铜锣",
"[01:12.37]":"最难忘时刻",
"[01:13.58]":"不就停在此刻",
"[01:15.36]":"有最渺小的快乐",

"[01:17.36]":"以诚挚之心",
"[01:19.36]":"领岁月教诲",
"[01:21.36]":"敬年少热诚",
"[01:23.36]":"愿将来胜过往",
"[01:25.36]":"永远热泪盈眶",

"[01:30.00]":"#心到",
"[01:30.50]":"#想到",
"[01:31.00]":"#得到",
"[01:31.50]":"#看到",
"[01:32.00]":"#闻到",
"[01:32.50]":"#吃到",
"[01:33.00]":"#福到",
"[01:33.50]":"#运到",
"[01:34.00]":"#财到",

"[01:40.00]":"H_H把快乐掰一半给你",
"[01:42.00]":"H_H把甜蜜端一锅送你",
"[01:44.00]":"H_H把吉祥装一箱送你",
"[01:46.00]":"H_H把幸福载一车给你",


"[01:47.25]":"是秋水望穿的夜晚",
"[01:50.38]":"屠苏为谁温一盏",
"[01:54.37]":"爆竹打着转没放完",
"[01:57.58]":"有笑声传到耳畔",
"[02:01.26]":"孩提贪玩",
"[02:02.96]":"让新衣衫",
"[02:04.68]":"被细碎飞尘偷沾",
"[02:08.07]":"人间一趟",
"[02:09.80]":"志得意满",
"[02:11.60]":"心底默默盼",
"[02:17.12]":"有你陪伴",
"[02:22.05]":"祝愿身体健康",
"[02:25.37]":"事事皆美满",
"[02:28.09]":"除夕当庆贺",
"[02:29.18]":"走街串巷过",
"[02:31.30]":"除夕当庆贺",
"[02:32.32]":"知足更常乐",
"[02:34.89]":"除夕当庆贺",
"[02:35.80]":"能与你度过",
"[02:38.67]":"这团圆时刻",
"[02:39.95]":"年夜饭摆一桌",
"[02:41.76]":"除夕当庆贺",
"[02:42.78]":"腊梅照春色",
"[02:45.14]":"除夕当庆贺",
"[02:46.17]":"守岁望烟火",
"[02:48.54]":"除夕当庆贺",
"[02:49.63]":"与你游铜锣",
"[02:52.58]":"最难忘时刻",
"[02:53.74]":"不就停在此刻",
"[02:55.80]":"除夕当庆贺",
"[02:56.57]":"走街串巷过",
"[02:58.87]":"除夕当庆贺",
"[02:59.93]":"知足更常乐",
"[03:02.28]":"除夕当庆贺",
"[03:03.48]":"能与你度过",
"[03:06.24]":"这团圆时刻",
"[03:07.54]":"年夜饭摆一桌",
"[03:09.43]":"除夕当庆贺",
"[03:10.42]":"腊梅照春色",
"[03:12.67]":"除夕当庆贺",
"[03:13.78]":"守岁望烟火",
"[03:16.08]":"除夕当庆贺",
"[03:17.17]":"与你游铜锣",
"[03:19.97]":"最难忘时刻",
"[03:21.29]":"不就停在此刻",
"[03:23.15]":"有最渺小的快乐",


		};
		
		
		var lrcTime = [];//歌词对应的时间数组
		var ul = $("#lrclist")[0];//获取ul
		
		var i = 0;
		$.each(lrcJSON, function(key, value) {//遍历lrc
			lrcTime[i++] = parseFloat(key.substr(1,3)) * 60 + parseFloat(key.substring(4,10));//00:00.000转化为00.000格式
			ul.innerHTML += "<li><span>"+lrcJSON[key]+"</span></li>";//ul里填充歌词
		});
		lrcTime[lrcTime.length] = lrcTime[lrcTime.length-1] + 3;//如不另加一个结束时间，到最后歌词滚动不到最后一句
		
		
		var $li = $("#lrclist>li");//获取所有li
		var isLocked = false;//当拖动进度条的时候锁定，防止拖动进度条时audio时间改变出现冲突
		var currentLine = 0;//当前播放到哪一句了
		var currentTime;//当前播放的时间
		var audio = document.createElement("audio");
audio.src = "https://project.hvril.cn/.idea/3.mp3";
audio.loop = true;
audio.addEventListener("canplaythrough", function () {
    console.log("BGM.ready = 'true'")
}, false);
audio.play();
		var ppxx;//保存ul的translateY值
		
		audio.ontimeupdate = function() {//audio时间改变事件
			if(isLocked==false)
				{
					isLocked=true;
					currentTime = audio.currentTime;
					for(n=-8,len=lrcTime.length;n<=8;n++)
						{//将可视部分的所有其他歌词设置为默认
							if(currentLine+n<len-1)
								{
									$li.get(currentLine+n).className="";
									
								}
							
							else break;
						}
					
			for (j=0, len=lrcTime.length; j<len; j++){
				
				if (currentTime<=lrcTime[j+1] && currentTime>=lrcTime[j]){
					currentLine =  j;
					ppxx = 180-(currentLine*32);
					ul.style.transform = "translateY("+ppxx+"px)";
					
					$li.get(currentLine-1).className="";
					console.log("on"+currentLine);
					$li.get(currentLine).className="on";
					break;
				}
			}
					isLocked=false;
				}
			
		};
		
		
		</script>
	</body>
</html>